package beautiful.butterfly.drds.data_exchange.reader.util;

import beautiful.butterfly.drds.data_exchange.exception.DataExchangeException;
import beautiful.butterfly.drds.data_exchange.reader.Constant;
import beautiful.butterfly.drds.data_exchange.reader.Key;
import beautiful.butterfly.drds.data_exchange.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public final class OriginalConfPretreatmentUtil
{


    public static void doPretreatment(Configuration configuration) throws SQLException
    {
        // 检查 username/password 配置（必填）
        configuration.getNecessaryValue(Key.username, DBUtilErrorCode.REQUIRED_VALUE);
        configuration.getNecessaryValue(Key.password, DBUtilErrorCode.REQUIRED_VALUE);
        dealWhere(configuration);

        simplifyConf(configuration);
    }

    public static void dealWhere(Configuration configuration)
    {
        String where = configuration.getString(Key.where, null);
        if (StringUtils.isNotBlank(where))
        {
            String whereImprove = where.trim();
            if (whereImprove.endsWith(";") || whereImprove.endsWith("；"))
            {
                whereImprove = whereImprove.substring(0, whereImprove.length() - 1);
            }
            configuration.set(Key.where, whereImprove);
        }
    }

    /**
     * 对配置进行初步处理：
     * <ol>
     * <li>处理同一个数据库配置了多个jdbcUrl的情况</li>
     * <li>识别并标记是采用querySql 模式还是 tableName 模式</li>
     * <li>对 tableName 模式，确定分表个数，并处理 table_column_name_list 转 *事项</li>
     * </ol>
     */
    private static void simplifyConf(Configuration configuration) throws SQLException
    {
        boolean isTableMode = recognizeTableOrQuerySqlMode(configuration);
        configuration.set(Constant.IS_TABLE_MODE, isTableMode);

        dealJdbcAndTable(configuration);

        dealColumnConf(configuration);
    }

    private static void dealJdbcAndTable(Configuration configuration)
    {
        String username = configuration.getString(Key.username);
        String password = configuration.getString(Key.password);
        boolean checkSlave = configuration.getBoolean(Key.check_slave, false);
        boolean isTableMode = configuration.getBoolean(Constant.IS_TABLE_MODE);
        boolean isPreCheck = configuration.getBoolean(Key.dry_run, false);

        List<Object> conns = configuration.getList(Constant.CONN_MARK,
                Object.class);
        List<String> preSql = configuration.getList(Key.pre_sql_list, String.class);

        int tableNum = 0;

        for (int i = 0, size = conns.size(); i < size; i++)
        {
            Configuration configuration1 = Configuration.from(conns.get(i).toString());

            configuration1.getNecessaryValue(Key.jdbc_url, DBUtilErrorCode.REQUIRED_VALUE);

            List<String> jdbcUrls = configuration1.getList(Key.jdbc_url, String.class);

            String jdbcUrl;
            if (isPreCheck)
            {
                jdbcUrl = Jdbcs1.chooseJdbcUrl(jdbcUrls,
                        username, password, preSql, checkSlave);
            } else
            {
                jdbcUrl = Jdbcs1.chooseJdbcUrlWithRetry(jdbcUrls,
                        username, password, preSql, checkSlave);
            }


            // 回写到connection[i].jdbcUrl
            configuration.set(String.format("%s[%d].%s", Constant.CONN_MARK, i, Key.jdbc_url), jdbcUrl);

            log.info("Available jdbcUrl:{}.", jdbcUrl);

            if (isTableMode)
            {
                // tableName 方式
                // 对每一个connection 上配置的table 项进行解析(已对表名称进行了 ` 处理的)
                List<String> tableNameList = configuration1.getList(Key.table_name, String.class);

                List<String> expandedTables = TableExpandUtil.expand(
                        tableNameList);

                if (null == expandedTables || expandedTables.isEmpty())
                {
                    throw DataExchangeException.asDataExchangeException(
                            DBUtilErrorCode.ILLEGAL_VALUE, String.format("您所配置的读取数据库表:%s 不正确. 因为DataX根据您的配置找不到这张表. 请检查您的配置并作出修改." +
                                    "请先了解 DataX 配置.", StringUtils.join(tableNameList, ",")));
                }

                tableNum += expandedTables.size();

                configuration.set(String.format("%s[%d].%s",
                        Constant.CONN_MARK, i, Key.table_name), expandedTables);
            } else
            {
                // 说明是配置的 querySql 方式，不做处理.
            }
        }

        configuration.set(Constant.TABLE_NUMBER_MARK, tableNum);
    }

    private static void dealColumnConf(Configuration configuration) throws SQLException
    {
        boolean isTableMode = configuration.getBoolean(Constant.IS_TABLE_MODE);

        List<String> userConfiguredColumnNameList = configuration.getList(Key.column,
                String.class);

        if (isTableMode)
        {
            if (null == userConfiguredColumnNameList
                    || userConfiguredColumnNameList.isEmpty())
            {
                throw DataExchangeException.asDataExchangeException(DBUtilErrorCode.REQUIRED_VALUE, "您未配置读取数据库表的列信息. " +
                        "正确的配置方式是给 table_column_name_list 配置上您需要读取的列名称,用英文逗号分隔. 例如: \"table_column_name_list\": [\"id\", \"name\"],请参考上述配置并作出修改.");
            } else
            {
                String splitKey = configuration.getString(Key.split_key, null);

                if (1 == userConfiguredColumnNameList.size()
                        && "*".equals(userConfiguredColumnNameList.get(0)))
                {
                    log.warn("您的配置文件中的列配置存在一定的风险. 因为您未配置读取数据库表的列，当您的表字段个数、类型有变动时，可能影响任务正确性甚至会运行出错。请检查您的配置并作出修改.");
                    // 回填其值，需要以 String 的方式转交后续处理
                    configuration.set(Key.column, "*");
                } else
                {
                    String jdbcUrl = configuration.getString(String.format(
                            "%s[0].%s", Constant.CONN_MARK, Key.jdbc_url));

                    String username = configuration.getString(Key.username);
                    String password = configuration.getString(Key.password);

                    String tableName = configuration.getString(String.format(
                            "%s[0].%s[0]", Constant.CONN_MARK, Key.table_name));

                    List<String> columnNameList = Jdbcs1.getColumnNameList(jdbcUrl, username, password, tableName);
                    log.info("tableName:[{}] has columnNameList:[{}].", tableName, StringUtils.join(columnNameList, ","));

                    List<String> newColumnNameList = new ArrayList<String>();

                    for (String columnName : userConfiguredColumnNameList)
                    {
                        if ("*".equals(columnName))
                        {
                            throw DataExchangeException.asDataExchangeException(
                                    DBUtilErrorCode.ILLEGAL_VALUE,
                                    "您的配置文件中的列配置信息有误. 因为根据您的配置，数据库表的列中存在多个*. 请检查您的配置并作出修改. ");
                        }

                        newColumnNameList.add(columnName);
                    }

                    configuration.set(Key.column_list, newColumnNameList);
                    configuration.set(Key.column, StringUtils.join(newColumnNameList, ","));
                    if (StringUtils.isNotBlank(splitKey))
                    {
                        if (!columnNameList.contains(splitKey.toLowerCase()))
                        {
                            throw DataExchangeException.asDataExchangeException(DBUtilErrorCode.ILLEGAL_SPLIT_PK,
                                    String.format("您的配置文件中的列配置信息有误. 因为根据您的配置，您读取的数据库表:%s 中没有主键名为:%s. 请检查您的配置并作出修改.", tableName, splitKey));
                        }
                    }

                }
            }
        } else
        {
            // querySql模式，不希望配制 table_column_name_list，那样是混淆不清晰的
            if (null != userConfiguredColumnNameList
                    && userConfiguredColumnNameList.size() > 0)
            {
                log.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 table_column_name_list. 如果您不想看到这条提醒，请移除您源头表中配置中的 table_column_name_list.");
                configuration.remove(Key.column);
            }

            // querySql模式，不希望配制 where，那样是混淆不清晰的
            String where = configuration.getString(Key.where, null);
            if (StringUtils.isNotBlank(where))
            {
                log.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 where. 如果您不想看到这条提醒，请移除您源头表中配置中的 where.");
                configuration.remove(Key.where);
            }

            // querySql模式，不希望配制 splitPk，那样是混淆不清晰的
            String splitkey = configuration.getString(Key.split_key, null);
            if (StringUtils.isNotBlank(splitkey))
            {
                log.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 splitPk. 如果您不想看到这条提醒，请移除您源头表中配置中的 splitPk.");
                configuration.remove(Key.split_key);
            }
        }

    }

    private static boolean recognizeTableOrQuerySqlMode(
            Configuration originalConfig)
    {
        List<Object> conns = originalConfig.getList(Constant.CONN_MARK,
                Object.class);

        List<Boolean> tableModeFlags = new ArrayList<Boolean>();
        List<Boolean> querySqlModeFlags = new ArrayList<Boolean>();

        String tableName = null;
        String querySql = null;

        boolean isTableMode = false;
        boolean isQuerySqlMode = false;
        for (int i = 0, size = conns.size(); i < size; i++)
        {
            Configuration configuration = Configuration
                    .from(conns.get(i).toString());
            tableName = configuration.getString(Key.table_name, null);
            querySql = configuration.getString(Key.query_sql, null);

            isTableMode = StringUtils.isNotBlank(tableName);
            tableModeFlags.add(isTableMode);

            isQuerySqlMode = StringUtils.isNotBlank(querySql);
            querySqlModeFlags.add(isQuerySqlMode);

            if (false == isTableMode && false == isQuerySqlMode)
            {
                // tableName 和 querySql 二者均未配制
                throw DataExchangeException.asDataExchangeException(
                        DBUtilErrorCode.TABLE_QUERYSQL_MISSING, "您的配置有误. 因为table和querySql应该配置并且只能配置一个. 请检查您的配置并作出修改.");
            } else if (true == isTableMode && true == isQuerySqlMode)
            {
                // tableName 和 querySql 二者均配置
                throw DataExchangeException.asDataExchangeException(DBUtilErrorCode.TABLE_QUERYSQL_MIXED,
                        "您的配置凌乱了. 因为datax不能同时既配置table又配置querySql.请检查您的配置并作出修改.");
            }
        }

        // 混合配制 tableName 和 querySql
        if (!ListUtil.checkIfValueSame(tableModeFlags)
                || !ListUtil.checkIfValueSame(tableModeFlags))
        {
            throw DataExchangeException.asDataExchangeException(DBUtilErrorCode.TABLE_QUERYSQL_MIXED,
                    "您配置凌乱了. 不能同时既配置table又配置querySql. 请检查您的配置并作出修改.");
        }

        return tableModeFlags.get(0);
    }

}
